﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace System.Peppers.Wmi
{
    /// <summary>
    /// The Win32_Product WMI class represents products as they are installed by Windows Installer. A product generally correlates to one installation package.
    /// </summary>
    public sealed class Product : CimSetting
    {
        #region Static
        private static class WmiProperties
        {
            public const string IdentifyingNumber = "IdentifyingNumber",
                InstallLocation = "InstallLocation",
                InstallState = "InstallState",
                Name = "Name",
                PackageCache = "PackageCache",
                PackageName = "PackageName",
                SKUNumber = "SKUNumber",
                Vendor = "Vendor",
                Version = "Version";
        }

        private static class WmiMethods
        {
            public const string Install = "Install",
                Admin = "Admin",
                Advertise = "Advertise",
                Configure = "Configure",
                Reinstall = "Reinstall",
                Upgrade = "Upgrade",
                Uninstall = "Uninstall";
        }

        private static class WmiParameters
        {
            public const string PackageLocation = "PackageLocation",
                Options = "Options",
                AllUsers = "AllUsers",
                ReinstallMode = "ReinstallMode",
                InstallState = "InstallState",
                InstallLevel = "InstallLevel";
        }

        private static class Util
        {
            public static Version ToVersion(object value)
            {
                string text = value as string;
                if (string.IsNullOrEmpty(text))
                {
                    return null;
                }

                try
                {
                    return new Version(text);
                }
                catch
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Enumerates installed product objects on the system
        /// </summary>
        /// <returns>List of installed product objects</returns>
        public static IEnumerable<Product> GetInstalledProducts()
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_Product))
            {
                yield return new Product(item);
            }
        }

        /// <summary>
        /// Enumerates windows installed product on a remote system
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <returns>List of installed product objects</returns>
        public static IEnumerable<Product> GetInstalledProducts(WmiRemoteParameters remoteParams)
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_Product, remoteParams))
            {
                yield return new Product(item);
            }
        }

        /// <summary>
        /// The static Install WMI class method installs an associated Win32_Product instance using the installation package provided through the PackageLocation parameter, and any supplied command line options.
        /// </summary>
        /// <param name="packageLocation">The path to the package to be installed.</param>
        /// <param name="options">The command line options for the installation. Format as property=setting.</param>
        /// <param name="allUsers">This parameter value must be present, but it is ignored. True is the recommended value.</param>
        /// <returns>Result of method</returns>
        public static ManagementStatus Install(string packageLocation, string options, bool allUsers)
        {
            Product product = new Product(GetStaticInstance(WmiClasses.Win32_Product));
            return product.ExecuteStatusMethod(WmiMethods.Install, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.PackageLocation] = packageLocation;
                inParam[WmiParameters.Options] = options;
                inParam[WmiParameters.AllUsers] = allUsers;
            });
        }

        /// <summary>
        /// The static Install WMI class method installs an associated Win32_Product instance using the installation package provided through the PackageLocation parameter, and any supplied command line options.
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <param name="packageLocation">The path to the package to be installed.</param>
        /// <param name="options">The command line options for the installation. Format as property=setting.</param>
        /// <param name="allUsers">This parameter value must be present, but it is ignored. True is the recommended value.</param>
        /// <returns>Result of method</returns>
        public static ManagementStatus Install(WmiRemoteParameters remoteParams, string packageLocation, string options, bool allUsers)
        {
            using (Product product = new Product(GetStaticInstance(WmiClasses.Win32_Product, remoteParams)))
            {
                return product.ExecuteStatusMethod(WmiMethods.Install, delegate(ManagementBaseObject inParam)
                {
                    inParam[WmiParameters.PackageLocation] = packageLocation;
                    inParam[WmiParameters.Options] = options;
                    inParam[WmiParameters.AllUsers] = allUsers;
                });
            }
        }

        /// <summary>
        /// The Admin WMI class method performs an administrative install of an associated Win32_Product using the installation package provided through the PackageLocation parameter, and any supplied command line options.
        /// </summary>
        /// <param name="packageLocation">The path to the package to be installed.</param>
        /// <param name="options">The command line options for the installation. Format as property=setting.</param>
        /// <param name="allUsers">This parameter value must be present, but it is ignored. True is the recommended value.</param>
        /// <returns>Result of method</returns>
        public static ManagementStatus AdministrativeInstall(string packageLocation, string options, bool allUsers)
        {
            Product product = new Product(GetStaticInstance(WmiClasses.Win32_Product));
            return product.ExecuteStatusMethod(WmiMethods.Admin, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.PackageLocation] = packageLocation;
                inParam[WmiParameters.Options] = options;
                inParam[WmiParameters.AllUsers] = allUsers;
            });
        }

        /// <summary>
        /// The Admin WMI class method performs an administrative install of an associated Win32_Product using the installation package provided through the PackageLocation parameter, and any supplied command line options.
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <param name="packageLocation">The path to the package to be installed.</param>
        /// <param name="options">The command line options for the installation. Format as property=setting.</param>
        /// <param name="allUsers">This parameter value must be present, but it is ignored. True is the recommended value.</param>
        /// <returns>Result of method</returns>
        public static ManagementStatus AdministrativeInstall(WmiRemoteParameters remoteParams, string packageLocation, string options, bool allUsers)
        {
            using (Product product = new Product(GetStaticInstance(WmiClasses.Win32_Product, remoteParams)))
            {
                return product.ExecuteStatusMethod(WmiMethods.Admin, delegate(ManagementBaseObject inParam)
                {
                    inParam[WmiParameters.PackageLocation] = packageLocation;
                    inParam[WmiParameters.Options] = options;
                    inParam[WmiParameters.AllUsers] = allUsers;
                });
            }
        }

        /// <summary>
        /// The Advertise WMI class method advertises an associated Win32_Product instance using the installation package provided through the PackageLocation parameter and any supplied command line options.
        /// </summary>
        /// <param name="packageLocation">The path to the package to be advertised</param>
        /// <param name="options">Command line options for the advertisement. Format as property=setting.</param>
        /// <returns>Result of method</returns>
        public static ManagementStatus Advertise(string packageLocation, string options)
        {
            Product product = new Product(GetStaticInstance(WmiClasses.Win32_Product));
            return product.ExecuteStatusMethod(WmiMethods.Advertise, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.PackageLocation] = packageLocation;
                inParam[WmiParameters.Options] = options;
            });
        }

        /// <summary>
        /// The Advertise WMI class method advertises an associated Win32_Product instance using the installation package provided through the PackageLocation parameter and any supplied command line options.
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <param name="packageLocation">The path to the package to be advertised</param>
        /// <param name="options">Command line options for the advertisement. Format as property=setting.</param>
        /// <returns>Result of method</returns>
        public static ManagementStatus Advertise(WmiRemoteParameters remoteParams, string packageLocation, string options)
        {
            using (Product product = new Product(GetStaticInstance(WmiClasses.Win32_Product, remoteParams)))
            {
                return product.ExecuteStatusMethod(WmiMethods.Advertise, delegate(ManagementBaseObject inParam)
                {
                    inParam[WmiParameters.PackageLocation] = packageLocation;
                    inParam[WmiParameters.Options] = options;
                });
            }
        }
        #endregion

        internal Product(ManagementObject wmiObject)
            : base(wmiObject)
        { }

        /// <summary>
        /// Product identification such as a serial number on software, or a die number on a hardware chip.
        /// </summary>
        public string IdentifyingNumber
        {
            get { return Convert.ToString(this[WmiProperties.IdentifyingNumber]); }
        }

        /// <summary>
        /// Location of the installed product.
        /// </summary>
        public string InstallLocation
        {
            get { return Convert.ToString(this[WmiProperties.InstallLocation]); }
        }

        /// <summary>
        /// Installed state of the product.
        /// </summary>
        public ProductInstallState InstallState
        {
            get { return (ProductInstallState)Convert.ToInt16(this[WmiProperties.InstallState] ?? -1); }
        }

        /// <summary>
        /// Commonly used product name.
        /// </summary>
        public string Name
        {
            get { return Convert.ToString(this[WmiProperties.Name]); }
        }

        /// <summary>
        /// Location of the locally cached package for this product.
        /// </summary>
        public string PackageCache
        {
            get { return Convert.ToString(this[WmiProperties.PackageCache]); }
        }

        /// <summary>
        /// The original package name for the product.
        /// </summary>
        public string PackageName
        {
            get { return Convert.ToString(this[WmiProperties.PackageName]); }
        }

        /// <summary>
        /// Product SKU (stock-keeping unit) information.
        /// </summary>
        public string SkuNumber
        {
            get { return Convert.ToString(this[WmiProperties.SKUNumber]); }
        }

        /// <summary>
        /// Name of the product supplier. Corresponds to the Vendor property in the product object in the Desktop Management Task Force (DMTF) Solution Exchange Standard.
        /// </summary>
        public string Vendor
        {
            get { return Convert.ToString(this[WmiProperties.Vendor]); }
        }

        /// <summary>
        /// Product version information. Corresponds to the Version property in the product object in the DMTF Solution Exchange Standard.
        /// </summary>
        public Version Version
        {
            get { return Util.ToVersion(this[WmiProperties.Version]); }
        }

        /// <summary>
        /// Using the specified reinstallation mode, the Reinstall WMI class method reinstalls the associated instance of Win32_Product.
        /// </summary>
        /// <param name="reinstallMode">The reinstallation mode.</param>
        /// <returns>Result of method</returns>
        public ManagementStatus Reinstall(ProductReinstallMode reinstallMode)
        {
            return ExecuteStatusMethod(WmiMethods.Reinstall, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.ReinstallMode] = reinstallMode;
            });
        }

        /// <summary>
        /// The Upgrade WMI class method upgrades the associated Win32_Product instance using the upgrade package provided through the PackageLocation parameter and any supplied command line options.
        /// </summary>
        /// <param name="packageLocation">The path to the package that contains the upgrade.</param>
        /// <param name="options">Command line options for the advertisement. Format as property=setting.</param>
        /// <returns>Result of method</returns>
        public ManagementStatus Upgrade(string packageLocation, string options)
        {
            return ExecuteStatusMethod(WmiMethods.Upgrade, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.PackageLocation] = packageLocation;
                inParam[WmiParameters.Options] = options;
            });
        }

        /// <summary>
        /// The Configure WMI class method configures the associated instance of Win32_Product to the specified install state and level.
        /// </summary>
        /// <param name="installState">Installation state.</param>
        /// <param name="installLevel">Installation level.</param>
        /// <param name="options">The command line options for configure. These should be in the form of property=setting.</param>
        /// <returns>Result of method</returns>
        public ManagementStatus Configure(ProductConfigureState installState, ProductInstallLevel installLevel, string options)
        {
            return ExecuteStatusMethod(WmiMethods.Configure, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.InstallState] = installState;
                inParam[WmiParameters.InstallLevel] = installLevel;
                inParam[WmiParameters.Options] = options;
            });
        }

        /// <summary>
        /// The Uninstall WMI class method uninstalls the associated instance of Win32_Product.
        /// </summary>
        /// <returns>Result of method</returns>
        public ManagementStatus Uninstall()
        {
            return ExecuteStatusMethod(WmiMethods.Uninstall, null);
        }
    }
}